#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/identity.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_generators.hpp>
#include <boost/uuid/uuid_io.hpp>

#define CHUNKS 600000

struct Data {
    long long id;
    std::wstring filename;
    std::wstring filepath;
    template<class Archive>
    void serialize(Archive & ar, const unsigned int version) {
        ar & filename;
        ar & filepath;
        ar & id;
    }
};
typedef boost::multi_index::multi_index_container<
    Data,
    boost::multi_index::indexed_by<
        boost::multi_index::ordered_unique<
            boost::multi_index::member<Data, long long, &Data::id>
        >,
        boost::multi_index::ordered_non_unique<
            boost::multi_index::member<Data, std::wstring, &Data::filename>
        >
    >
> DataContainer;
std::mutex mtx;
void SaveData(DataContainer& data, bool nocheck=false) {
    DataContainer save_data;
    if(!nocheck)
    {
        std::lock_guard<std::mutex> lock(mtx);
        if(data.size()<CHUNKS) return;
        data.swap(save_data);
    }
    if(save_data.size()){
        boost::uuids::uuid uuid = boost::uuids::random_generator()();
        std::string filename = boost::uuids::to_string(uuid);
        if(!std::filesystem::exists("data")) std::filesystem::create_directory("data");
        std::ofstream ofs(std::filesystem::path("data")/filename, std::ios::binary);
        boost::archive::binary_oarchive oa(ofs);
        oa << save_data;
    }
}
void AddData(DataContainer& data, std::wstring filename, std::wstring filepath) {
    static long long id = 0;
    std::lock_guard<std::mutex> lock(mtx);
    data.insert({id++,filename,filepath});
}
////////////////////////////////////////////////////////////////////////////////////////////////
DataContainer LoadData(const std::string& filename) {
    DataContainer data;
    std::ifstream ifs(filename, std::ios::binary);
    if (ifs) {
        boost::archive::binary_iarchive ia(ifs);
        ia >> data;
    }
    return data;
}
void DeleteData(DataContainer& data, long long id) {
    auto& idIndex = data.get<0>();
    auto it = idIndex.find(id);
    if (it != idIndex.end()) {
        idIndex.erase(it);
    }
}
void UpdateData(DataContainer& data, long long id, const std::wstring& filepath, const std::wstring& filename) {
    auto& idIndex = data.get<0>();
    auto it = idIndex.find(id);
    if (it != idIndex.end()) {
        Data updatedData = *it;
        updatedData.filepath = filepath;
        updatedData.filename = filename;
        idIndex.replace(it, updatedData);
    }
}
std::list<Data> QueryData(DataContainer& data, std::wstring name) {
    std::list<Data> result;
    auto& idIndex = data.get<1>();
    auto range = idIndex.equal_range(name);
    for (auto it = range.first; it != range.second; ++it) 
        result.emplace_back(*it);

    return result;
}